package clock.socoolby.com.clock.widget.animatorview.animator.textanimator;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.List;

import clock.socoolby.com.clock.widget.animatorview.AbstractAnimator;
import clock.socoolby.com.clock.widget.animatorview.I_AnimatorEntry;

//引用自:https://www.jianshu.com/p/adda53b14e51

public class EZLedAnimator extends AbstractAnimator<EZLedAnimator.EZLed> {

    public static final String Tag=EZLedAnimator.class.getSimpleName();

    public static final String NAME="EZLed";

    /**
     * The max size of a bitmap
     */
    private static final int TEXTURE_MAX = 4 * 1024;
    /**
     * Led light show shape
     * 1.circle shape
     * 2.square shape
     * 3.custom shape
     */
    public static final String LED_TYPE_CIRCLE = "1";
    public static final String LED_TYPE_SQUARE = "2";
    public static final String LED_TYPE_DRAWABLE = "3";

    /**
     * Content type,text or image
     */
    public static final String CONTENT_TYPE_TEXT = "1";
    public static final String CONTENT_TYPE_IMAGE = "2";

    public EZLedAnimator() {
        super(0);
    }

    private String ledText="test";
    private int textSize=10;

    @Override
    public int delayTime() {
        return 20;
    }

    @Override
    public EZLed createNewEntry() {
        return new EZLed(width,height,0.5f,ledText,textSize);
    }

    public void setText(String text,int textSize){
        ledText=text;
        this.textSize=textSize;
        if(list.size()>0)
            list.get(0).setLedTextAndSize(text,textSize);
        else
            addText(text,0.5f,textSize);
    }

    public void addText(String text,float hightPercent,int textSize){
        list.add(new EZLed(width,height,hightPercent,text,textSize));
    }

    public class EZLed implements I_AnimatorEntry {

        private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);

        /**
         * Led light space
         */
        private int ledSpace;

        /**
         * Led light radius
         */
        private int ledRadius;
        /**
         * Led color, if content is image,this param not work
         */
        private int ledColor;

        /**
         * Content text size
         */
        private int ledTextSize;

        /**
         * Content type, text or image
         */
        private String ledType;

        /**
         * Custom led light drawable
         */
        private Drawable customLedLightDrawable;

        /**
         * Store the points of all px
         */
        private List<Point> circlePoint = new ArrayList<>();

        /**
         * Content of text
         */
        private CharSequence ledText;

        /**
         * Content of image
         */
        private Drawable ledImage;

        /**
         * The content width and height
         */
        private int mDrawableWidth;
        private int mDrawableHeight;

        private int mMaxWidth = Integer.MAX_VALUE;
        private int mMaxHeight = Integer.MAX_VALUE;
        private boolean sCompatAdjustViewBounds;

        private int startX=0;
        private float hightPercent=0;

        private String contentType = CONTENT_TYPE_TEXT;

        private boolean mustRefush=true;

        public EZLed(int mMaxWidth,int mMaxHeight,float hightPercent,String text,int textSize) {
            this.mMaxHeight=mMaxHeight;
            this.mMaxWidth=mMaxWidth;
            this.hightPercent =hightPercent;
            this.ledTextSize = textSize;
            init();
            setText(text);
        }

        private void init() {
            final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
            sCompatAdjustViewBounds = targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR1;
            paint.setStyle(Paint.Style.FILL);
            paint.setColor(Color.BLACK);

            ledRadius = 10;
            ledSpace = 5;


            ledColor =  color;
            ledType = LED_TYPE_CIRCLE;
            contentType = CONTENT_TYPE_TEXT;

            int ledImageId =  0;
            if (ledImageId != 0) {
                ledImage = context.getResources().getDrawable(ledImageId);
            }

            paint.setColor(ledColor);
            paint.setTextSize(ledTextSize);
        }

        /**
         * Set text content
         * @param text content
         */
        public void setText(CharSequence text) {
            timber.log.Timber.d("setText:"+text);
            this.contentType = CONTENT_TYPE_TEXT;
            this.ledText = text;
            measureTextBound(text.toString());
            mustRefush=true;
        }


        /**
         * Set drawable content
         * @param drawable drawable
         */
        public void setDrawable(Drawable drawable) {
            this.contentType = CONTENT_TYPE_IMAGE;
            this.ledImage = drawable;
            mDrawableWidth = drawable.getIntrinsicWidth();
            mDrawableHeight = drawable.getIntrinsicHeight();
            mustRefush=true;
        }


        /**
         * measure the text width and height
         * @param text text content
         */
        private void measureTextBound(String text) {
            Paint.FontMetrics m = paint.getFontMetrics();
            mDrawableWidth = (int) paint.measureText(text);
            mDrawableHeight = (int) (m.bottom - m.ascent);
        }


        float stepPercent=0.05f;
        @Override
        public void move(int maxWidth, int maxHight) {

            rightToLeft(maxWidth,maxHight);
        }

        private void leftToRight(int maxWidth, int maxHight){
            if(mustRefush)
                startX=0;
            Float len=maxWidth*stepPercent;
            startX=startX+len.intValue();
            if(startX>maxWidth+mDrawableWidth)
                startX=0;
        }

        private void rightToLeft(int maxWidth, int maxHight){
            if(mustRefush)
                startX=maxWidth;
            Float len=maxWidth*stepPercent;
            startX=startX-len.intValue();
            timber.log.Timber.d("move right to left startX:"+startX);
            if(startX<-mDrawableWidth)
                startX=maxWidth;
        }

        Bitmap bitmap=null;
        @Override
        public void onDraw(Canvas canvas, Paint mPaint) {
            if(bitmap==null||mustRefush) {
                if (contentType.equals(CONTENT_TYPE_TEXT)) {
                    bitmap = generateDrawable(renderText(ledText, paint));
                } else if (contentType.equals(CONTENT_TYPE_IMAGE)) {
                    bitmap = generateDrawable(renderDrawable(ledImage, mMaxHeight, mMaxWidth));
                }
                mustRefush=false;
            }
            timber.log.Timber.d("onDraw bitmap is :"+bitmap);
            float y=height* hightPercent -mDrawableHeight/2;
            if (bitmap != null) {
                if(bitmap.getWidth() > TEXTURE_MAX){
                    int count = (int) (Math.ceil(bitmap.getWidth()/(float)TEXTURE_MAX));
                    for (int i =0; i < count; i++){
                        int x = i * TEXTURE_MAX;
                        int width = TEXTURE_MAX;
                        if(x + width > bitmap.getWidth()){
                            width = bitmap.getWidth() - x;
                        }
                        Bitmap newBitmap = Bitmap.createBitmap(bitmap,x,0,width,bitmap.getHeight());
                        canvas.drawBitmap(newBitmap,startX,y,paint);
                    }
                }else {
                    canvas.drawBitmap(bitmap,startX,y,paint);
                }
            }
        }

        @Override
        public void setAnimatorEntryColor(int color) {
            this.ledColor=color;
            paint.setColor(ledColor);
        }


        /**
         * Transform text to bitmap
         * @param text text content
         * @param paint paint
         * @return the bitmap of text
         */

        private Bitmap renderText(CharSequence text, Paint paint) {
            timber.log.Timber.d("renderText:"+text);
            Bitmap cacheMap = Bitmap.createBitmap(mDrawableWidth, mDrawableHeight, Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(cacheMap);
            int yPos = (int) ((canvas.getHeight() / 2) - ((paint.descent() + paint.ascent()) / 2));
            canvas.drawText(text.toString(),0,yPos,paint);
            return cacheMap;
        }

        private boolean mustRefash(){
            return mustRefush;
        }

        private Bitmap generateDrawable(Bitmap bitmap) {
            timber.log.Timber.d("generateDrawable:"+bitmap);
            if (bitmap != null) {
                release();
                measureBitmap(bitmap);
                return generateLedBitmap(bitmap);
            }
            return null;
        }

        /**
         * Transform a bitmap to a led bitmap
         * @param src the original bitmap
         * @return led bitmap
         */
        private Bitmap generateLedBitmap(Bitmap src) {
            timber.log.Timber.d("generateLedBitmap:"+src);
            Bitmap bitmap = Bitmap.createBitmap(src.getWidth(), src.getHeight(), Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(bitmap);
            for (Point point : circlePoint) {
                // Detect if the px is in range of our led position
                int color = isInRange(src, point.x, point.y);
                if (color != 0) {
                    if (ledColor != 0 && !contentType.equals(CONTENT_TYPE_IMAGE)) {
                        color = ledColor;
                    }
                    paint.setColor(color);

                    // draw shape according to ledType
                    if (LED_TYPE_CIRCLE.equals(ledType)) {
                        canvas.drawCircle(point.x, point.y, ledRadius, paint);
                    } else if (LED_TYPE_SQUARE.equals(ledType)) {
                        canvas.drawRect(point.x - ledRadius, point.y - ledRadius, point.x + ledRadius, point.y + ledRadius, paint);
                    } else if (LED_TYPE_DRAWABLE.equals(ledType)) {
                        customLedLightDrawable.setBounds(point.x - ledRadius, point.y - ledRadius, point.x + ledRadius, point.y + ledRadius);
                        customLedLightDrawable.draw(canvas);
                    }
                }
            }
            return bitmap;
        }

        public void release() {
            circlePoint.clear();
        }


        private void measureBitmap(Bitmap bitmap) {
            measurePoint(bitmap.getWidth(), bitmap.getHeight());
        }


        /**
         * Calculate the led point position
         */
        private void measurePoint(int width, int height) {
            timber.log.Timber.d("measurePoint width:"+width+"height:"+height);
            int halfBound = ledRadius + ledSpace / 2;
            int x = halfBound;
            int y = halfBound;
            for (; ; ) {
                for (; ; ) {
                    circlePoint.add(new Point(x, y));
                    y += halfBound * 2;
                    if (y > height) {
                        y = halfBound;
                        break;
                    }
                }
                x += halfBound * 2;
                if (x > width) {
                    break;
                }
            }
        }

        private int isInCircleLeft(Bitmap bitmap, int x, int y) {
            if (x - ledRadius > 0 && x - ledRadius < bitmap.getWidth()
                    && y > 0 && y < bitmap.getHeight()) {
                int pxL = bitmap.getPixel(x - ledRadius, y);
                if (pxL != 0)
                    return pxL;
            }
            return 0;
        }

        private int isInCircleTop(Bitmap bitmap, int x, int y) {
            if (y - ledRadius > 0 && y - ledRadius < bitmap.getHeight()
                    && x > 0 && x < bitmap.getWidth()) {
                int pxT = bitmap.getPixel(x, y - ledRadius);
                if (pxT != 0)
                    return pxT;
            }
            return 0;
        }

        private int isInCircleRight(Bitmap bitmap, int x, int y) {
            if (x + ledRadius > 0 && x + ledRadius < bitmap.getWidth()
                    && y > 0 && y < bitmap.getHeight()) {
                int pxR = bitmap.getPixel(x + ledRadius, y);
                if (pxR != 0)
                    return pxR;
            }
            return 0;
        }


        private int isInCircleBottom(Bitmap bitmap, int x, int y) {
            if (y + ledRadius > 0 && y + ledRadius < bitmap.getHeight()
                    && x > 0 && x < bitmap.getWidth()) {
                int pxB = bitmap.getPixel(x, y + ledRadius);
                if (pxB != 0)
                    return pxB;
            }
            return 0;
        }

        private int isInCircleCenter(Bitmap bitmap, int x, int y) {
            if (y > 0 && y < bitmap.getHeight()
                    && x > 0 && x < bitmap.getWidth()) {
                int pxC = bitmap.getPixel(x, y);
                if (pxC != 0)
                    return pxC;
            }
            return 0;
        }

        /**
         * Measure if x and y is in range of leds
         * @param bitmap the origin bitmap
         * @param x led x
         * @param y led y
         * @return the color , if color is zero means empty
         */
        private int isInRange(Bitmap bitmap, int x, int y) {
            if (bitmap == null)
                return 0;
            int pxL = isInCircleLeft(bitmap, x, y);
            int pxT = isInCircleTop(bitmap, x, y);
            int pxR = isInCircleRight(bitmap, x, y);
            int pxB = isInCircleBottom(bitmap, x, y);
            int pxC = isInCircleCenter(bitmap, x, y);

            int num = 0;
            if (pxL != 0) {
                num++;
            }
            if (pxT != 0) {
                num++;
            }
            if (pxR != 0) {
                num++;
            }
            if (pxB != 0) {
                num++;
            }
            if (pxC != 0) {
                num++;
            }
            if (num >= 2) {
                int a = Color.alpha(pxL) + Color.alpha(pxT) + Color.alpha(pxR) + Color.alpha(pxB) + Color.alpha(pxC);
                int r = Color.red(pxL) + Color.red(pxT) + Color.red(pxR) + Color.red(pxB) + Color.red(pxC);
                int g = Color.green(pxL) + Color.green(pxT) + Color.green(pxR) + Color.green(pxB) + Color.green(pxC);
                int b = Color.blue(pxL) + Color.blue(pxT) + Color.blue(pxR) + Color.blue(pxB) + Color.blue(pxC);
                return Color.argb(a / 5, r / 5, g / 5, b / 5);
            }

            return 0;
        }

        public int getLedSpace() {
            return ledSpace;
        }

        public void setLedSpace(int ledSpace) {
            this.ledSpace = ledSpace;
            mustRefush=true;
        }

        public int getLedRadius() {
            return ledRadius;
        }

        public void setLedRadius(int ledRadius) {
            this.ledRadius = ledRadius;
        }

        public int getLedColor() {
            return ledColor;
        }

        public void setLedColor(int ledColor) {
            this.ledColor = ledColor;
            paint.setColor(ledColor);
            mustRefush=true;
        }

        public int getLedTextSize() {
            return ledTextSize;
        }

        public void setLedTextAndSize(String text,int ledTextSize){
            this.ledText=text;
            setLedTextSize(ledTextSize);
        }

        public void setLedTextSize(int ledTextSize) {
            if(ledText == null)
                throw new NullPointerException("Please set ledText before setLedTextSize");
            this.ledTextSize = ledTextSize;
            measureTextBound(ledText.toString());
            paint.setTextSize(ledTextSize);
            mustRefush=true;
        }

        public String getLedType() {
            return ledType;
        }

        public void setLedType(String ledType) {
            this.ledType = ledType;
            mustRefush=true;
        }

        public Drawable getLedLightDrawable() {
            return customLedLightDrawable;
        }

        public void setLedLightDrawable(Drawable ledLightDrawable) {
            this.customLedLightDrawable = ledLightDrawable;
            mustRefush=true;
        }

        public CharSequence getLedText() {
            return ledText;
        }

        public void setLedText(CharSequence ledText) {
            this.ledText = ledText;
            mustRefush=true;
        }

    }


    /**
     * Transform the image drawable to bitmap
     * @param drawable the content drawable
     * @param width the new bitmap width
     * @param height the new bitmap height
     * @return bitmap of drawable
     */
    private static Bitmap renderDrawable(Drawable drawable, int width, int height) {
        Bitmap bitmap = getBitmapFromDrawable(drawable);
        return Bitmap.createScaledBitmap(bitmap, width, height, true);
    }

    @Deprecated
    private static Bitmap loadBitmapFromView(View v) {
        if (v.getMeasuredHeight() <= 0 || v.getLayoutParams() == null) {
            v.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            Bitmap b = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
            Canvas c = new Canvas(b);
            v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
            v.draw(c);
            return b;
        }
        Bitmap b = Bitmap.createBitmap(v.getLayoutParams().width, v.getLayoutParams().height, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(b);
        v.layout(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
        v.draw(c);
        return b;
    }

    /**
     * Get bitmap from drawable, Copy from CircleImageView
     * @param drawable the drawable
     * @return the bitmap of drawable
     */
    private static Bitmap getBitmapFromDrawable(Drawable drawable) {
        if (drawable == null) {
            return null;
        }

        if (drawable instanceof BitmapDrawable) {
            return ((BitmapDrawable) drawable).getBitmap();
        }

        try {
            Bitmap bitmap;

            if (drawable instanceof ColorDrawable) {
                bitmap = Bitmap.createBitmap(2, 2, Bitmap.Config.ARGB_8888);
            } else {
                bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ALPHA_8);
            }

            Canvas canvas = new Canvas(bitmap);
            drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
            drawable.draw(canvas);
            return bitmap;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
